#define _CRT_SECURE_NO_WARNINGS 1
#include"text.h"
 
//隐藏光标+控制台大小
static void set_window() {
	system("mode con cols=100 lines=30");
	system("title 贪吃蛇");
	//获取句柄
	HANDLE output = GetStdHandle(STD_OUTPUT_HANDLE);
	//定义光标结构体来存储当前句柄的光标信息
	CONSOLE_CURSOR_INFO cursor_info = { 0 };
	//获取句柄光标信息
	GetConsoleCursorInfo(output, &cursor_info);
	//隐藏光标
	cursor_info.bVisible = FALSE;
	SetConsoleCursorInfo(output, &cursor_info);
	//设置窗口固定大小
	// 获取当前控制台窗口的句柄
	HWND consoleWindow = GetConsoleWindow();
	// 获取当前窗口的样式
	LONG_PTR windowStyle = GetWindowLongPtr(consoleWindow, GWL_STYLE);
	// 禁止用户调整窗口大小，移除WS_SIZEBOX、WS_MAXIMIZEBOX、WS_MINIMIZEBOX样式
	SetWindowLongPtr(consoleWindow, GWL_STYLE, windowStyle & ~(WS_SIZEBOX | WS_MAXIMIZEBOX | WS_MINIMIZEBOX));//移除按钮和边框这样就不能改变大小
	// 应用新的窗口样式
	SetWindowPos(consoleWindow, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
}
//定位光标
void set_pos(int x,int y) {
 
	//给一个坐标
	COORD pos = { x,y };
	//获取句柄
	HANDLE output = GetStdHandle(STD_OUTPUT_HANDLE);
	//设置坐标位置
	SetConsoleCursorPosition(output, pos);//设置控制台光标位置
}
//欢迎开始界面
static void wecometogame() {
	set_pos(36, 10);
	wprintf(L"欢迎来到贪吃蛇游戏\n");
	set_pos(36, 25);
	system("pause");
	system("cls");
	set_pos(40, 10);
	wprintf(L"游戏介绍\n");
	set_pos(32, 12);
	wprintf(L"用↑ ↓ ← →键位控制方向，F3(加速) F4(减速)\n");
	set_pos(32, 14);
	wprintf(L"加速可得到更高的分数\n");
	set_pos(36, 25);
	system("pause");
	system("cls");
 
}
//修改控制台字符颜色
static void Textcolor(WORD   wAttributes) {
	//获取句柄
	HANDLE output = GetStdHandle(STD_OUTPUT_HANDLE);
	SetConsoleTextAttribute(output, wAttributes);
 
}
//地图打印
void Drawmap() {
	Textcolor(FOREGROUND_RED);
	int i = 0;
	for (i = 0 ; i < 29; i++)
	{
		wprintf(L"%lc", WALL_l);
	}
 
	set_pos(0, 26);
	for (i =0; i < 29; i++)
	{
		wprintf(L"%lc", WALL_l);
	}
 
	for ( i = 0; i <= 25; i++)
	{
		set_pos(0, i);
		wprintf(L"%lc", WALL_l);
	}
 
	for (i = 0; i <= 25; i++)
	{
		set_pos(56, i);
		wprintf(L"%lc", WALL_l);
	}
}
//创建蛇
void Create_snake(psnake ps) {
	psnakeNode_Hand cur = NULL;
	for (int i = 0; i < 3; i++)
	{
		//申请节点
		cur = (psnakeNode_Hand)malloc(sizeof(snakeNode));
		if (cur == NULL)
		{
			perror("Create_snake::malloc");
			return;
		}
		//设置坐标
		cur->next = NULL;
		cur->x = pos_x + i * 2;//*2是移动两格，避免重叠(宽字符)
		cur->y = pos_y;
		
		//利用头插将每个节点串在一起
		if (ps->Head == NULL)
		{
			ps->Head = cur;//将节点直接给到head的位置
		}
		else
		{
			cur->next = ps->Head;
			ps->Head = cur;
		}
	}
	//打印蛇的身体
	Textcolor(FOREGROUND_BLUE);
	cur = ps->Head;
	while (cur)
	{
		set_pos(cur->x, cur->y);
		wprintf(L"%c", BODY);
		cur = cur->next;
	}
 
	//初始化贪吃蛇的数据
	ps->dir = RIGHT;
	ps->food_scores = 10;
	ps->sleep_time = 200;
	ps->state = OK;
	ps->sum = 0;
	
}
//创建食物
void Create_food(psnake ps) {
	int x, y;
again:
	//生成随机坐标
	do
	{
		x = rand() % 53 + 2;
		y = rand() % 25 + 1;
	} while (x % 2 != 0);//x坐标必须是2的倍数(偶数)
 
	//判断x,y的坐标和蛇是否冲突
	psnakeNode_Hand cur = ps->Head;
	while (cur)
	{
		if ((cur->x == x) && (cur->y == y)) {//将他们的坐标比较
			goto again;//相等直接重新生成坐标
		}
		cur = cur->next;//每一个节点
	}
 
	psnakeNode_Hand pfood = (psnakeNode_Hand)malloc(sizeof(snakeNode));
	if (pfood == NULL)
	{
		perror("Create_food::malloc");
		return;
	}
	else
	{
		//设置食物颜色
		Textcolor(FOREGROUND_GREEN);
		//赋值随机生成的坐标
		pfood->x = x;
		pfood->y = y;
		set_pos(pfood->x, pfood->y);
		wprintf(L"%lc", FOOD);//打印
		ps->food = pfood;
	}
	
 
}
//初始化
void Game_start(psnake ps) {
	//时间戳
	srand((unsigned int)time(NULL));
//--隐藏光标+控制台大小--
	set_window();
//--打印开始界面+功能介绍--
	wecometogame();
//--绘制地图--
	Drawmap();
//--创建蛇--//--设置游戏相关信息--
	Create_snake(ps);
//--创建食物--
	Create_food(ps);
}
 
//====================================================
//游戏提示
void Game_prompt() {
	set_pos(75, 7);
	wprintf(L"%ls", L"《游戏提示》\n");
	set_pos(64, 10);
	wprintf(L"%ls", L"<不能穿墙，不能咬到自己>\n");
	set_pos(64, 12);
	wprintf(L"%ls", L"<用↑.↓.←.→分别控制蛇的移动>\n");
	set_pos(64, 14);
	wprintf(L"%ls", L"<F3 为加速，F4 为减速>\n");
	set_pos(64, 16);
	wprintf(L"%ls", L"<ESC ：退出游戏.space：暂停游戏>\n");
	set_pos(80, 18);
	wprintf(L"%ls",L"--普通小青年\n");
	
}
 
//判断是否遇到食物
int Nextfood(psnakeNode_Hand pn, psnake ps) {
	
	return (pn->x == ps->food->x && pn->y == ps->food->y);//都满足就返回1，否者返回0
}
 
//吃掉食物
void Eatfood(psnakeNode_Hand pn, psnake ps) {
	//头插
	pn->next = ps->Head;
	ps->Head = pn;
	
	psnakeNode_Hand cur = ps->Head;
	while (cur)
	{
		Textcolor(FOREGROUND_BLUE);
		set_pos(cur->x, cur->y);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
	ps->sum += ps->food_scores;//吃掉一个食物加分
	free(ps->food);//销毁食物
	Create_food(ps);//再次生成一个食物
}
 
//没有食物
void Nofood(psnakeNode_Hand pn, psnake ps) {
	//psnakeNode_Hand pn -- 下一个节点地址
	//psnake ps 维护蛇的指针
	
	//头插
	pn->next = ps->Head;
	ps->Head = pn;
 
	psnakeNode_Hand cur = ps->Head;
	
	//找到倒数第二个节点地址
	while (cur->next->next)
	{
		set_pos(cur->x,cur->y);
		Textcolor(FOREGROUND_BLUE);
		wprintf(L"%lc", BODY);
		cur = cur->next;
	}
	Textcolor(FOREGROUND_GREEN);
	set_pos(cur->next->x,cur->next->y);
	printf("  ");
	free(cur->next);
	cur->next = NULL;
 
}
 
//判断撞墙
void KillByWall(psnake ps) {
	//判断头部是否和我们的墙的坐标一样
	if (
		(ps->Head->x == 0) ||
		(ps->Head->x == 56)||
		(ps->Head->y == 0)||
		(ps->Head->y == 26)
		)
	{
		//游戏状态
		ps->state = WALL;
		return 1;
 
	}
	return 0;
}
 
//判断撞到自己
void KillBySelf(psnake ps) {
	//遍历坐标如果有自己子节点的坐标
	psnakeNode_Hand cur = ps->Head->next;//遍历除了头节点的其他节点所以我们要ps->Head->next,也就是第二个节点开始遍历
	while (cur)
	{
		if (
			(ps->Head->x == cur->x)
			&&(ps->Head->y == cur->y)
			) {
 
			ps->state = TOUCH_YOURSELF;
			return 1;
		}
		cur = cur->next;
	}
	return 0;
}
 
//蛇的移动
void SnakeMove(psnake ps) {
	//创建下一个位置的节点
	psnakeNode_Hand pNextNode = (psnakeNode_Hand)malloc(sizeof(snakeNode));
	if (pNextNode ==NULL)
	{
		perror("SnakeMove::malloc");
		return;
	}
	//确定下一个节点的坐标
	switch (ps->dir) {
	case UP:
		pNextNode->x = ps->Head->x;
		pNextNode->y = ps->Head->y - 1;
		break;
	case DOWN:
		pNextNode->x = ps->Head->x;
		pNextNode->y = ps->Head->y + 1;
		break;
	case LEFT:
		pNextNode->x = ps->Head->x - 2;
		pNextNode->y = ps->Head->y;
		break;
	case RIGHT:
		pNextNode->x = ps->Head->x + 2;
		pNextNode->y = ps->Head->y;
		break;
	}
	//判断我们要走的下一个节点是不是食物
	if (Nextfood(pNextNode,ps))//写一个判断下一个节点是不是食物
	{
		Eatfood(pNextNode,ps);
	}
	else
	{
		Nofood(pNextNode, ps);
	}
	
 
	//判断撞墙
	KillByWall(ps);    
	//判断撞到自己
	KillBySelf(ps);
}
 
//游戏暂停
void Game_stop() {
	
	while (1)
	{
		if (KEY_PRESS(VK_SPACE)) {
			break;
		}
		Sleep(200);
	}
}
 
void Game_run(psnake ps) {
	//游戏提示
	Game_prompt();
	do
	{
		//打印分数和食物分值
		set_pos(64, 9);
		printf("总分：%2d ", ps->sum);
		printf("食物分值：%2d\n", ps->food_scores);
		
		if (KEY_PRESS(VK_UP) && ps->dir != DOWN) {
			ps->dir = UP;
		}
		else if(KEY_PRESS(VK_DOWN) && ps->dir != UP)
		{
			ps->dir = DOWN;
		}
		else if (KEY_PRESS(VK_LEFT) && ps->dir != RIGHT)
		{
			ps->dir = LEFT;
		}
		else if (KEY_PRESS(VK_RIGHT) && ps->dir != LEFT)
		{
			ps->dir = RIGHT;
		}
		else if (KEY_PRESS(VK_SPACE) )
		{
			//游戏暂停
			Game_stop();
		}
		else if (KEY_PRESS(VK_ESCAPE) )
		{
			//退出游戏
			ps->state = END;
		}
		else if (KEY_PRESS(VK_F3))
		{
			//加速
			if (ps->sleep_time>=80) {
				ps->sleep_time -= 30;//时间越短数度越快
				ps->food_scores += 2;
			}
		}
		else if (KEY_PRESS(VK_F4))
		{
			//减速
			if (ps->food_scores>2) {
				ps->sleep_time += 30;//时间越大数度越慢
				ps->food_scores -= 2;
			}
		}
 
		Sleep(ps->sleep_time);
		//蛇的移动	
		SnakeMove(ps);
 
	} while (ps->state == OK);//游戏状态
}
 
//结束游戏
void Game_end(psnake ps) {
	psnakeNode_Hand cur = ps->Head;
 
	set_pos(24,12);
	if (ps->state == END) {
		printf("你主动退出游戏\n");
	}
	else if( ps->state == WALL)
	{
		printf("撞到墙了\n");
	}
	else if(ps->state == TOUCH_YOURSELF)
	{
		printf("咬到自己了\n");
	}
 
	//释放蛇身的节点
	while (cur)
	{
		psnakeNode_Hand del = cur;
		cur = cur->next;
		free(del);
	}
 
}

